#!/usr/bin/python
# -*- coding: utf-8 -*-

import re
import os
from time import localtime
from random import randrange
from random import shuffle
from config import Config
from BeautifulSoup import BeautifulSoup

def readScoreList():
    scores = {}
    scoreFile = os.path.join(Config['path'],'kontext',Config['scoreFile'])
    if not os.path.exists(scoreFile): return False
    for x in open(scoreFile,"r").read().split(): scores[x.split(':')[0]] = int(x.split(':')[1])
    return scores

def removeTags(text,tag='<.*?>',remover=''):
    return re.sub(tag, remover, str(text))

def getAllTags(text):
    return list(set(map(lambda x: x.strip('</>').split()[0],re.findall('<.*?>',str(text)))))

def mark(text,word):
    return text.replace(word,"<b>%s</b>" % word)

def now():
    return ':'.join(map(lambda x: str(x),localtime()[3:6]))

def otisticSort(dicto,rev=True):
    x = [(v,k) for (k,v) in dicto.items()]
    x.sort(reverse=rev)
    x = [(k,v) for (v,k) in x]
    return x

def highLight(content,wordList):
    for word in wordList:
        content.replace(word,"<b>%s</b>"%word)
    return content

class Kontext:

    def __init__(self,content,searchString,options=None):
        self.scoreList = readScoreList()
        self.content = BeautifulSoup(content)
        self.log = logger()
        
        # self.searchStrings = unicode(searchString).strip("'").split()
        # Get the search criteria posibilities..
        b = unicode(searchString).strip("'").split()
        if len(b)>1:
            ret = []
            for i in range(len(b)):
                for j in range(1,len(b)+1):
                    ret.append(' '.join(b[i:j]))
            ret = list(set(ret))
            ret.remove('')
        else:
            ret = b

        self.searchStrings = ret
        self.log.add("Given search strings: %s" % ','.join(ret))
        
        #self.allTags = getAllTags(self.content)
        
        self.options = {'method':'M1',
                        'sorted':'true',
                        'limit':3000,
                        'mark':'true',
                        'showScores':'false',
                        'debug':'false',
                        'format':'html',
                        'random':'false'}
        
        if options:
            opts = options.split(',')
            opts = [x.split(':') for x in opts]
            for opt in opts:
                if opt[1].isdigit(): opt[1] = int(opt[1])
            self.options.update(dict(opts))
        
        self.log.add("Given options: "+str(self.options))
        
    def initialize(self):
        self.log.add("Started to initialize")
        
        self.pageStruct = {}
        
        # Parts of document
        
        self.log.add("Started to create page structure")
        if self.content.title:
            self.pageStruct['title']   = self.content.title.renderContents()
            self.log.add("Title found : %s" % self.pageStruct['title'])
        
        self.pageStruct['body']    = self.content.body
        self.log.add("Body found")
        
        if self.pageStruct['body']:
            # Create document Structure for given tags
            for key in self.scoreList.keys():
                self.log.add("%s tags is searching in content" % key)
                self.pageStruct[key] = self.pageStruct['body'].findAll(key)
        else:
            return False

        return True
    
    def render(self):
        debugPrefix = "<div style='clear:right ;float:right;'>"\
                      "<a href='#' onclick='tV(document.getElementById(\"debugLogs\"), this); return false'>show debug</a></div>"

        if not self.scoreList:
            return "No scoring file !!"

        documentHasStructure = self.initialize()

        if len(self.searchStrings)==0:
            return self.content

        self.log.add("Started to rendering")

        self.log.add("Render Method selected as %s, starting to rendering with this method" % self.options['method'])

        if documentHasStructure:
            if self.options['method'] == 'M1':
                res = self.renderP1()
                if len(removeTags(res)) < 10 :
                    self.log.add("For given method M1 it is not possible to create a summary, trying to method M2")
                    res = self.renderP2()
                    if len(res) < 10:
                        res = self.renderP4()
                        if len(res) < 10:
                            self.log.add("For given page it is not possible to create a summary")
                            return "This page is not a static page and it is not possible to create a summary"
            elif self.options['method'] == 'M2':
                res = self.renderP2()
            elif self.options['method'] == 'M3':
                res = self.renderP3()
        else:
            self.log.add("Document has no structure for M1,M2 or M3 trying to use M4")
            res = self.renderP4()
            if not res:
                return "Document has no structure (or not an HTML Document), summarization is not possible."

        self.log.add("Showing the content")
        
        if self.options['format'] == 'plain':
            self.log.add("Format selected as plain, HTML tags removing")
            res = removeTags(res)
        
        if self.options['limit'] == '-1':
            self.log.add("No Summarization limit given, full summarization will show")
            self.options['limit'] = -1
        else:
            self.log.add("Summarization limit is setted as %d, limiting" % self.options['limit'])
        
        res = res[0:self.options['limit']]+"..."
        
        if self.options['mark'] == 'true':
            self.log.add("Marking selected, searchStrings are marking")
            pattern = "<span class='hi'>%s</span>"
            for criteria in self.searchStrings:
                res = res.replace(criteria,pattern % criteria)
                res = res.replace(criteria.lower(),pattern % criteria.lower())
                res = res.replace(criteria.capitalize(),pattern % criteria.capitalize())
                res = res.replace(criteria.upper(),pattern % criteria.upper())
        
        retText = res
        
        if self.options['debug']=='true':
            retText = debugPrefix + retText + self.log.get()
            
        return retText
    
    def renderP1(self):
        self.log.add("Method M1 Started")
        result = nodes(self.searchStrings,self.log)
        
        # Head Search and scoring for well-structured Documents
        heads = ['h1','h2','h3','h4']
        for tag in heads:
            self.log.add("Searching ['%s']s related to searchStrings" % tag)
            for head in self.pageStruct[tag]:
                for criteria in self.searchStrings:
                    if unicode(criteria).lower() in unicode(head.renderContents()).lower():
                        headNode = node(head,self.scoreList[tag],priority=self.scoreList[tag])
                        childNode = None
                        self.log.add("Found a '%s' is related to searchStrings : %s..." %
                                     (tag,
                                      headNode.text[0:40]))
                        if head.nextSibling:
                            childNode = node(head.nextSibling.next,score=5,parent=headNode,priority=self.scoreList[tag]-1)
                            self.log.add("Found an childNode is related to Header : %s..." %
                                         childNode.text[0:40])
                            headNode.child=childNode
                        result.add(headNode)
                        if childNode:
                            result.add(childNode)
        
        self.log.add("Searching paragraphs related to searchStrings")
        for criteria in self.searchStrings:
            for paragraph in self.pageStruct['p']:
                try:
                    if unicode(criteria).lower() in unicode(paragraph.renderContents()).lower():
                        pNode = node(paragraph,self.scoreList['p'])
                        result.add(pNode)
                        self.log.add("Found a paragraph is related to searchStrings : %s... " % pNode.text[0:40].replace('\n',''))
                except:
                    pass
        
        if len(result.nodes)>1:
            self.log.add("Started to summarization")
            return result.asText(self.options,self.log)
        return ''

    def renderP2(self):
        self.log.add("Method M2 Started")
        result = nodes(self.searchStrings,self.log)
        baseScore = 6
        bodyContent = removeTags(unicode(self.pageStruct['body']).replace('<br>','\n'))
        sentences = bodyContent.split('\n')
        
        self.log.add("Searching sentences related to searchStrings")
        for criteria in self.searchStrings:
            for sentence in sentences:
                if unicode(criteria).lower() in unicode(sentence).lower():
                    Node = node(sentence,baseScore)
                    self.log.add("Found a sentence is related to searchStrings : %s... " % sentence.replace('\n',''))
                    result.add(Node)
        
        self.log.add("Started to summarization")
        return result.asText(self.options,self.log,linker=' ')
    
    def renderP3(self):
        self.log.add("Method M3 Started")
        result = nodes(self.searchStrings,self.log)
        bodyContent = removeTags(unicode(self.pageStruct['body']).replace('<br>','\n'))
        sentences = bodyContent.split('\n')
        
        self.log.add("Searching sentences related to searchStrings, scoring randomly")
        for criteria in self.searchStrings:
            for sentence in sentences:
                if unicode(criteria).lower() in unicode(sentence).lower():
                    baseScore = randrange(100)
                    self.log.add("Random score : %s " % baseScore)
                    Node = node(sentence,baseScore)
                    self.log.add("Found a sentence is related to searchStrings : %s... " % sentence.replace('\n',''))
                    result.add(Node)
        self.options['sorted']='false'
        self.options['random']='true'
        self.log.add("Started to summarization")
        return result.asText(self.options,self.log,linker=' ')

    def renderP4(self):
        if not self.content.html:
            return False
        self.log.add("Method M4 Started")
        result = nodes(self.searchStrings,self.log)
        baseScore = 6
        bodyContent = removeTags(unicode(self.content).replace('<br>','\n'))
        sentences = bodyContent.split('\n')
        
        self.log.add("Searching sentences related to searchStrings")
        for criteria in self.searchStrings:
            for sentence in sentences:
                if unicode(criteria).lower() in unicode(sentence).lower():
                    Node = node(sentence,baseScore)
                    self.log.add("Found a sentence is related to searchStrings : %s... " % sentence.replace('\n',''))
                    result.add(Node)
        
        self.log.add("Started to summarization")
        return result.asText(self.options,self.log,linker=' ')

class nodes:
    def __init__(self,searchStrings,logger):
        self.nodes = []
        self.searchStrings = searchStrings
        self.log = logger

    def add(self,_node):
        if _node.text not in map(lambda x: x.text,self.nodes):
            self.log.add("New node is added to list with score: %s" %_node.score)
            self.nodes.append(_node)
        else:
            aNode = self.get(_node.text)
            self.log.add("Node already in list with score: %s and new score is %s " %(aNode.score,aNode.score*_node.score))
            aNode.score *= _node.score
            if _node.child and self.get(_node.child.text):
                self.get(_node.child.text).score = self.get(_node.text).score

    def sorted(self):
        for node in self.nodes:
            if node.parent:
                if node.parent.score < node.score:
                    node.parent.score = node.score +1
                else:
                    node.score = node.parent.score -1
        
        for node in self.nodes:
            node.score += node.priority

        x = [(v,k) for (k,v) in map(lambda x: (x.text,x.score),self.nodes)]
        x.sort(reverse=True)
        x = [(k,v) for (v,k) in x]
        return x
    
    def asText(self,options,logs,linker=''):
        self.log = logs
        if options['sorted'] == 'true':
            self.log.add("Sorted way selected, started to sorting")
            values = self.sorted()
            if options['showScores'] == 'true':
                self.log.add("showScores selected, scores adding to content")
                hugeText = linker.join(map(lambda x: str("<b>Score : [%d]</b>%s" %(self.get(x[0]).score,self.get(x[0]).val)),values))
            else:
                hugeText = linker.join(map(lambda x: str(self.get(x[0]).val),values))
        else:
            self.log.add("UnSorted way selected, will get just related content")
            values = self.nodes
            if options['random'] == 'true':
                shuffle(values)
            if options['showScores'] == 'true':
                self.log.add("showScores selected, scores adding to content")
                hugeText = linker.join(map(lambda x: str("<b>Score : [%d]</b>%s" %(x.score,x.val)),values))
            else:
                hugeText = linker.join(map(lambda x: str(x.val),values))
        
        return hugeText

    def summarized(self):
        pass
    
    def get(self,text):
        for node in self.nodes:
            if node.text == text:
                return node

class node:
    def __init__(self,val,score=0,parent=None,child=None,priority=0):
        self.val = val
        self.text = removeTags(val)
        self.score = score
        self.child = child
        self.parent = parent
        self.priority = priority

class logger:
    def __init__(self):
        self.prefix="<br><div id='debugLogs' style='display:none;color:#999;'><pre>"
        self.body = self.prefix
        
    def add(self,msg,type="debug"):
        self.body += "<b>%s</b>::%s::%s\n" % (type.upper(),now(),msg)
        
    def get(self):
        return self.body+"</pre></div>"
